﻿using System;
using SoftwareConsulting.BI11.NumericalMethods.Interfaces;
using SoftwareConsulting.BI11.NumericalMethods.DhbFunctionEvaluation;

namespace SoftwareConsulting.BI11.NumericalMethods.Iterations
{
    /// <summary>
    /// Класс незавершенной гамма-функции
    /// </summary>
    public class IncompleteGammaFunction : IOneVariableFunction
    {	    
        private double _alpha;//параметр функции	    
        private double _alphaLogGamma;//вычисляем только 1 раз	    
        private IncompleteGammaFunctionSeries _series;//бесконечный ряд	    
        private IncompleteGammaFunctionContinuedFraction _fraction;//непрерывная дробь

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="alpha">параметр функции</param>
        public IncompleteGammaFunction(double alpha)
        {
            _alpha = alpha;
            _alphaLogGamma = GammaFunction.LogGamma(_alpha);
        }

        /// <summary>
        /// Возвращает значение дроби при указанном аргументе
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private double GetFraction(double x)
        {
            if (_fraction == null)
            {
                _fraction = new IncompleteGammaFunctionContinuedFraction(_alpha);
                _fraction.SetDesiredPrecision(MathExtended.DefaultNumericalPrecision);
            }
            _fraction.Argument = x;
            _fraction.Evaluate();
            return _fraction.Result;
        }

        /// <summary>
        /// Возвращает сумму ряда при указанном аргументе
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private double GetSeriesSum(double x)
        {
            if (_series == null)
            {
                _series = new IncompleteGammaFunctionSeries(_alpha);
                _series.SetDesiredPrecision(MathExtended.DefaultNumericalPrecision);
            }
            _series.Argument = x;
            _series.Evaluate();
            return _series.Sum;
        }

	    /// <summary>
	    /// Возвращает значение функции при указанном аргументе
	    /// </summary>
	    /// <param name="x"></param>
	    /// <returns></returns>
        public double GetValue(double x)
        {
            if (x == 0)
                return 0;
            double norm = Math.Exp(Math.Log(x) * _alpha - x - _alphaLogGamma);
            return x - 1 < _alpha
                            ? GetSeriesSum(x) * norm
                            : 1 - norm / GetFraction(x);
        }
    }
}
